package drds.plus.datanode.select;

import drds.plus.datanode.configuration.DataSourceWrapper;
import drds.plus.datanode.executor.Executor;
import drds.plus.datasource.api.DataSource;

import java.sql.SQLException;
import java.util.Map;

/**
 * 在数据完全相同的一组库中选择一个库
 */
public interface Selector {

    String NOT_EXIST_USER_SPECIFIED_INDEX = "-1";

    /**
     * @return 返回该Selector的标识
     */
    String getId();

    /**
     * 对等数据库选择器。 在数据完全相同的一组库中选择一个库 用于对HA/RAC情况,多个读库中取一个读的操作
     */
    DataSourceWrapper select();

    /**
     * 返回指定dsKey对应的数据源。若对应数据源的当前权重为0，则返回null 这个方法同时可以用来判断一个dsKey对应的库是否可读或可写：
     * read selector.get(wBaseDsKey) != null 则可读 write selector.get(rBaseDsKey) != null 则可写
     * TGroupConnection读写连接复用的旧实现会用到这个功能
     *
     * @param dataSourceId 内部和每一个物理DataSource对应的key, 在初始化dbSelector时指定
     * @return 返回dsKey对应的数据源
     */
    DataSourceWrapper get(String dataSourceId);

    /**
     * 以选择到的DataSource和传入的args，重试执行 tryer.createConnectionWrapperAndExecute(String dsKey, DataSource
     * ds, Object... args) 每次选择DataSource会排除上次重试失败的, 直到达到指定的重试次数，或期间抛出非数据库不可用异常
     * 抛出异常后，以历次重试异常列表，和最初的args，调用 tryer.onSqlException(List<SQLException> exceptions, Object... args)
     */
    <T> T getConnectionWrapperAndExecuteAndRetryIfFailed(Executor<T> executor, int times, Object... args) throws SQLException;

    /**
     * @param failedDataSourceToSQLExceptionMap: 在调用该方法前，已经得知试过失败的DataSource和对应的SQLException
     *                                           存在这个参数的原因，是因为数据库操作割裂为getConnection/createDataNodeStatement/execute几步，而并不是在一个大的try
     *                                           catch中 failedDataSources == null 表示不需要重试，遇到任何异常直接抛出。如在写库上的操作
     */
    <T> T getConnectionWrapperAndExecuteAndRetryIfFailed(Map<javax.sql.DataSource, SQLException> failedDataSourceToSQLExceptionMap, Executor<T> executor, int times, Object... args) throws SQLException;

    boolean isSupportRetry();

    void setReadable(boolean readable);

    Map<String, DataSource> getDataSourceIdToDataSourceHolderMap(); // 直接获取对应的数据源

}
